/**
 * Project: apollo-base-commons
 * 
 * File Created at 2016年10月25日
 * 
 * Copyright 2015-2016 dx.com Croporation Limited.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * DongXue software Company. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with dx.com.
 */
package com.dx.pf.commons.async.proxy;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import com.dx.pf.commons.async.pool.AsyncFutureTask;
import com.dx.pf.commons.constants.AsyncConstant;
import com.dx.pf.commons.exceptions.AsyncException;
import com.dx.pf.commons.exceptions.AsyncTimeoutException;
import com.dx.pf.commons.log.Logger;
import com.dx.pf.commons.reflect.ClassUtil;
import com.dx.pf.commons.reflect.ReflectUtil;

/** 
* @ClassName: AsyncResultInterceptor 
* @Description: TODO(这里用一句话描述这个类的作用) 
* @author wuzhenfang(wzfbj2008@163.com)
* @date 2016年10月25日 下午4:47:43 
* @version V1.0 
*/
public class AsyncResultInterceptor  implements MethodInterceptor {

	private final static Logger logger = Logger.getLogger(AsyncResultInterceptor.class);

    private boolean runed;

    private AsyncFutureTask future;

    private long timeout;

    public AsyncResultInterceptor(AsyncFutureTask future, long timeout) {
	this.future = future;
	this.timeout = timeout;
    }

    private Object loadFuture() throws Throwable {
	long startRunTime = System.currentTimeMillis();
	try {
	    if (timeout <= 0) {
		return future.get();
	    } else {
		return future.get(timeout, TimeUnit.MILLISECONDS);
	    }
	}catch (TimeoutException e) {
	    future.cancel(true);
	    throw new AsyncTimeoutException("future invoke timeoutException",e);
	} catch (InterruptedException e) {
	    throw new AsyncException("funture invoke interruptedException",e);
	}catch (Exception e) {
	    throw getThrowableCause(e);
	} finally {
	    if (!runed) {
		logger.debug(future +" invoking time:"+(future.getEndTime() - future.getStartTime())+" timeout:"+timeout+",load time:" +(System.currentTimeMillis() - startRunTime));
	    }
	    runed = true;
	}
    }

    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
	if (AsyncConstant.ASYNC_DEFAULT_TRACE_LOG){
	    logger.debug("start call future:"+future+",object:"+object.getClass().getName()+" method:" + ClassUtil.buildMethod(method));
	}
	object = loadFuture();
	if (object != null) {
	    return ReflectUtil.invoke(object, args,method);
	}
	return null;
    }
    
    private Throwable getThrowableCause(Throwable e){
	if(e == null || e.getCause() == null){
	    return e; 
	}else{
	    Throwable throwable = getThrowableCause(e.getCause());
	    if(throwable == null){
		return e;
	    }else{
		return throwable;
	    }
	}
    }
}
